package mini.oo;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import mini.Env;
import mini.Proc;
import mini.ExpressionList;
import mini.expr.Element;
import mini.expr.Expr;

public class MiniClassInstantiation extends Expr {

    private MiniClassDefinition classDef;
    private Env env;
    
	public MiniClassInstantiation( List<Env> listOfEnvs, LinkedList var,
			MiniClassDefinition classDef, ExpressionList explist ) {
		
		this.classDef = classDef;
		
		// Better have the same number of expressions as we have parameters
		List<String> parameters = classDef.getParameters();
		List<Expr> expressions = new ArrayList<Expr>();
		if( explist != null && explist.getExpressions() != null ) {
			expressions = explist.getExpressions();
		}
		
		if( parameters.size() != expressions.size() ) {
			System.err.println( "Error in instantiating " + classDef.getName() );
			System.err.println( "Expected " + parameters.size() + " parameters, and passed " + expressions.size() );
			System.err.print( "Aborting" );
			System.exit(1);
		}
		
		// But we're happy, so set up an environment with the parameters....
		Env paramEnv = initializeEnvBasedOnParameters(listOfEnvs, var, parameters, expressions);
		
		// And use it to initialize the variables in this class
		env = (classDef == null ? new Env() : classDef.initializeEnvironment( paramEnv ) );
    }

	/**
	 * Create an environment that encapsulates the parameters we passed in
	 * 
	 * @param listOfEnvs
	 * @param var
	 * @param parameters
	 * @param expressions
	 * @return
	 */
	private Env initializeEnvBasedOnParameters(List<Env> listOfEnvs,
			LinkedList var, List<String> parameters, List<Expr> expressions) {
		Env paramEnv = new Env();
		for( int i=0; i < parameters.size(); i++ ) {
			String param = parameters.get(i);
			Expr expr = expressions.get(i);
			paramEnv.addToNametable(param, expr.eval(listOfEnvs, var));
		}
		return paramEnv;
	}

    public Element eval(List<Env> listOfEnvs, LinkedList var) {
    	return new Element(this);
    }
    
    public String toString() {
    	return "[Class " + classDef.getName() + " " + env.getNametable() + "]";
    }

    /**
     * Calls a method on this class, and returns the value as an element.
     * 
     * @param listOfEnvs
     * @param var
     * @param mName Method name to call
     * @param expressionlist Parameters to pass
     * @return Element that has the result
     */
	public Element callMethod(List<Env> listOfEnvs, LinkedList var, String mName, ExpressionList expressionlist) {
		
		Proc method = classDef.getMethod(mName);
		if( method == null ) {
			System.err.println( "Attempting to call method " + classDef.getName() + "." + mName + " but can find no such method" );
			System.err.println( "Aborting" );
			System.exit(1);
		}
		
		// Create an environment chain by appending this instantiations environment
		List<Env> newListOfEnvs = new ArrayList<Env>();
		newListOfEnvs.add(env);
		LinkedList newVar = new LinkedList();
		Element elem = method.apply(newListOfEnvs, newVar, expressionlist);
		return elem;
	}
}
